

#include "lua_navigate.h"
#include "finder/finder.h"

/********************************************************************************/

struct find_context {
  int i;
  lua_State* L;
};

/********************************************************************************/

static int luac_pushnext(lua_State* L) {
  size_t i  = luaL_checkunsigned(L, 1);
  size_t g  = luaL_checkunsigned(L, 2);
  size_t h  = luaL_checkunsigned(L, 3);
  int index = lua_upvalueindex(1); /* get the first up-value */
  const vertex* prev = (vertex*)lua_touserdata(L, index);
  push_vertex(i, prev, g, h);
  return 0;
}

/********************************************************************************/

static void on_neighbors(const vertex* prev, size_t finish, void* ud) {
  find_context* context = (find_context*)ud;
  lua_State* L = context->L;
  lua_pushvalue  (L, context->i); /* callback */
  lua_pushinteger(L, finish);
  lua_pushinteger(L, prev->i);
  lua_pushinteger(L, prev->g);
  lua_pushlightuserdata(L, (void*)prev);
  lua_pushcclosure(L, luac_pushnext, 1);
  if (lua_pcall(L, 4, 0, 0) != LUA_OK) {
    lua_error(L);
  }
}

/********************************************************************************/

static int luac_navigate(lua_State* L) {
  size_t begin = luaL_checkunsigned(L, 1);
  size_t end   = luaL_checkunsigned(L, 2);
  size_t depth = PF_DEPTH;
  int i = 3;
  if (lua_type(L, i) != LUA_TFUNCTION) {
    depth = luaL_checkunsigned(L, i);
    luaL_checktype(L, ++i, LUA_TFUNCTION);
  }
  find_context context;
  context.L = L;
  context.i = i;
  set_options(options::depth, depth);
  const vertex* pv = path_find(begin, end, on_neighbors, &context);
  if (pv == nullptr) {
    lua_pushnil(L);
    return 1;
  }
  lua_createtable(L, (int)pv->step, 0);
  for (int i = 1; pv; ++i, pv = pv->next()) {
    lua_pushinteger(L, pv->i);
    lua_rawseti(L, -2, i);
  }
  return 1;
}

/********************************************************************************/

LUAOS_API int luaopen_navigate(lua_State* L) {
  const luaL_Reg methods[] = {
    { "navigate", luac_navigate },
    { NULL,       NULL          }
  };
  return new_module(L, "math", methods);
}

/********************************************************************************/
